package com.cheche.dn.infrastructure.security;

import com.cheche.dn.domain.Apis;
import com.cheche.dn.domain.admin.Authority;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration
@EnableWebMvcSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new StandardPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        for (Apis.Mapping mapping : Apis.OPEN_MAPPINGS) {
            if (mapping.getMethod() != null) {
                http.authorizeRequests().antMatchers(mapping.getMethod(), mapping.getUrl()).permitAll();
            } else {
                http.authorizeRequests().antMatchers(mapping.getUrl()).permitAll();
            }
        }

        http.
                csrf().disable().logout().disable().
                sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
                and().
                authorizeRequests().
                antMatchers(Apis.SUPER_ADMIN_URL).hasAuthority(Authority.SUPER_ADMIN.getName()).
                anyRequest().authenticated().
                and().
                //anonymous().disable().
                exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint());

        for (Authority authority : Authority.values) {
            String method = authority.getMethod();
            if (method != null && !method.equals("")) {
                HttpMethod httpMethod = Enum.valueOf(HttpMethod.class, method);
                http.authorizeRequests().antMatchers(httpMethod, authority.getUrl()).hasAuthority(authority.getName());
            } else {
                http.authorizeRequests().antMatchers(authority.getUrl()).hasAuthority(authority.getName());
            }
        }

        http.addFilterBefore(new AuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class).
                addFilterBefore(new ManagementEndpointAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(domainUsernamePasswordAuthenticationProvider).
                authenticationProvider(backendAdminUsernamePasswordAuthenticationProvider).
                authenticationProvider(tokenAuthenticationProvider);
    }

    @Autowired
    @Qualifier("domainUsernamePasswordAuthenticationProvider")
    public AuthenticationProvider domainUsernamePasswordAuthenticationProvider;

    @Autowired
    @Qualifier("backendAdminUsernamePasswordAuthenticationProvider")
    private AuthenticationProvider backendAdminUsernamePasswordAuthenticationProvider;

    @Autowired
    @Qualifier("tokenAuthenticationProvider")
    public AuthenticationProvider tokenAuthenticationProvider;

    @Bean
    public AuthenticationEntryPoint unauthorizedEntryPoint() {
        return new AuthenticationEntryPoint() {

            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            }
        };
    }
}